home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / bin / xkeystone < prev    next >
Text File  |  2009-09-18  |  14KB  |  557 lines

  1. #!/usr/bin/env nickle
  2. /*
  3.  * Copyright ┬⌐ 2008 Keith Packard
  4.  *
  5.  * Permission to use, copy, modify, distribute, and sell this software and its
  6.  * documentation for any purpose is hereby granted without fee, provided that
  7.  * the above copyright notice appear in all copies and that both that copyright
  8.  * notice and this permission notice appear in supporting documentation, and
  9.  * that the name of the copyright holders not be used in advertising or
  10.  * publicity pertaining to distribution of the software without specific,
  11.  * written prior permission.  The copyright holders make no representations
  12.  * about the suitability of this software for any purpose.  It is provided "as
  13.  * is" without express or implied warranty.
  14.  *
  15.  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  16.  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
  17.  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  18.  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
  19.  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  20.  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  21.  * OF THIS SOFTWARE.
  22.  */
  23.  
  24. autoload Process;
  25. autoload Nichrome;
  26. autoload Nichrome::Box;
  27. autoload Nichrome::Label;
  28. autoload Nichrome::Button;
  29.  
  30. extend namespace Nichrome {
  31.     public namespace Quad {
  32.     public typedef quad_t;
  33.     public typedef widget_t + struct {
  34.         point_t[4]        p;
  35.         real        line_width;
  36.         real        corner_diameter;
  37.         rgba_color_t    line_color;
  38.         rgba_color_t    corner_color;
  39.         bool        down;
  40.         bool        started;
  41.         int            active;
  42.         void(&quad_t)   callback;
  43.     } quad_t;
  44.  
  45.     protected void outline (cairo_t cr, &quad_t quad) {
  46.         for (int i = 0; i < dim (quad.p); i++) {
  47.         arc (cr, quad.p[i].x, quad.p[i].y,
  48.              quad.corner_diameter / 2, 0, 2 * pi);
  49.         close_path (cr);
  50.         }
  51.     }
  52.  
  53.     void text_at (cairo_t cr, point_t p, string text) {
  54.         text_extents_t  e = text_extents (cr, text);
  55.         p.x = p.x - e.width / 2 - e.x_bearing;
  56.         p.y = p.y - e.height / 2 - e.y_bearing;
  57.         move_to (cr, p.x, p.y);
  58.         show_text (cr, text);
  59.     }
  60.  
  61.     protected void draw (cairo_t cr, &quad_t quad) {
  62.         if (!quad.started) {
  63.         quad.p[2].x = quad.p[1].x = quad.geometry.width;
  64.         quad.p[3].y = quad.p[2].y = quad.geometry.height;
  65.         quad.started = true;
  66.         }
  67.         rectangle (cr, 0, 0, quad.geometry.width, quad.geometry.height);
  68.         set_source_rgba (cr, 0, 0, 0, .25);
  69.         fill (cr);
  70.         for (int i = 0; i < dim (quad.p); i++)
  71.         line_to (cr, quad.p[i].x, quad.p[i].y);
  72.         close_path (cr);
  73.         set_line_width (cr, quad.line_width);
  74.         set_source_rgba (cr, quad.line_color.red, quad.line_color.green,
  75.                  quad.line_color.blue, quad.line_color.alpha);
  76.         set_line_join (cr, line_join_t.ROUND);
  77.         stroke (cr);
  78.         set_source_rgba (cr, quad.corner_color.red, quad.corner_color.green,
  79.                  quad.corner_color.blue, quad.corner_color.alpha);
  80.         outline (cr, &quad);
  81.         fill (cr);
  82.         set_source_rgba (cr, 1, 1, 1, 1);
  83.         for (int i = 0; i < dim (quad.p); i++)
  84.         text_at (cr, quad.p[i], sprintf ("%d", i));
  85.     }
  86.  
  87.     int nearest (&quad_t quad, point_t p) {
  88.         real    best_dist2 = 0;
  89.         int        best = 0;
  90.  
  91.         for (int i = 0; i < dim (quad.p); i++) {
  92.         real dist2 = ((p.x - quad.p[i].x) ** 2 +
  93.                   (p.y - quad.p[i].y) ** 2);
  94.         if (i == 0 || dist2 < best_dist2) {
  95.             best_dist2 = dist2;
  96.             best = i;
  97.         }
  98.         }
  99.         return best;
  100.     }
  101.  
  102.     protected void button (&quad_t quad, &button_event_t event) {
  103.         enum switch (event.type) {
  104.         case press:
  105.         quad.down = true;
  106.         quad.active = nearest (&quad, event);
  107.         break;
  108.         case release:
  109.         quad.down = false;
  110.         break;
  111.         default:
  112.         break;
  113.         }
  114.     }
  115.  
  116.     protected void motion (&quad_t quad, &motion_event_t motion) {
  117.         if (quad.down) {
  118.         motion.x = max (0, min (quad.geometry.width, motion.x));
  119.         motion.y = max (0, min (quad.geometry.height, motion.y));
  120.         quad.p[quad.active].x = motion.x;
  121.         quad.p[quad.active].y = motion.y;
  122.         quad.callback (&quad);
  123.         Widget::reoutline (&quad);
  124.         Widget::redraw (&quad);
  125.         }
  126.     }
  127.  
  128.     protected void configure (&quad_t quad,
  129.                   rect_t geometry)
  130.     {
  131.         if (quad.geometry.width > 0 && quad.geometry.height > 0)
  132.         {
  133.         real    x_scale = geometry.width / quad.geometry.width;
  134.         real    y_scale = geometry.height / quad.geometry.height;
  135.         for (int i = 0; i< 4; i++) {
  136.             quad.p[i].x *= x_scale;
  137.             quad.p[i].y *= y_scale;
  138.         }
  139.         }
  140.         Widget::configure (&quad, geometry);
  141.         quad.callback (&quad);
  142.     }
  143.     
  144.     protected void init (&quad_t quad,
  145.                  &nichrome_t nichrome,
  146.                  void (&quad_t) callback) {
  147.         Widget::init (&nichrome, &quad);
  148.         quad.outline = outline;
  149.         quad.draw = draw;
  150.         quad.button = button;
  151.         quad.motion = motion;
  152.         quad.configure = configure;
  153.         quad.p = (point_t[4]) {
  154.         { x = 0, y = 0 } ...
  155.         };
  156.         quad.line_color = (rgba_color_t) {
  157.         red = 1, green = 0, blue = 0, alpha = .5
  158.         };
  159.         quad.line_width = 10;
  160.         quad.corner_color = (rgba_color_t) {
  161.         red = 0, green = 0, blue = 1, alpha = 0.75
  162.         };
  163.         quad.corner_diameter = 20;
  164.         quad.down = false;
  165.         quad.active = -1;
  166.         quad.callback = callback;
  167.         quad.started = false;
  168.     }
  169.  
  170.     protected *quad_t new (&nichrome_t nichrome, void(&quad_t) callback) {
  171.         quad_t  quad;
  172.  
  173.         init (&quad, &nichrome, callback);
  174.         return &quad;
  175.     }
  176.     }
  177. }
  178. import Nichrome;
  179. import Nichrome::Box;
  180. import Nichrome::Label;
  181. import Nichrome::Button;
  182. import Nichrome::Quad;
  183.  
  184. import Cairo;
  185. typedef real[3,3]   m_t;
  186. typedef point_t[4]  q_t;
  187.  
  188. /*
  189.  * Ok, given an source quad and a dest rectangle, compute
  190.  * a transform that maps the rectangle to q. That's easier
  191.  * as the rectangle has some nice simple properties. Invert
  192.  * the matrix to find the opposite mapping
  193.  *
  194.  *  q0    q1
  195.  *
  196.  *  q3    q2
  197.  *
  198.  *  | m00 m01 m02 |
  199.  *  | m10 m11 m12 |
  200.  *  | m20 m21 m22 |
  201.  *
  202.  *  m [ 0 0 1 ] = q[0]
  203.  *
  204.  * Set m22 to 1, and solve:
  205.  *
  206.  *  |     m02       ,     m12       , 1 | = | q0x, q0y, 1 |
  207.  *
  208.  *  | m00 * w + q0x   m10 * w + q0y     |
  209.  *  | ------------- , ------------- , 1 | = | q1x, q1y, 1 |
  210.  *  |  m20 * w + 1     m20 * w + 1      |
  211.  
  212.  *   m00*w + q0x = q1x*(m20*w + 1)
  213.  *   m00 = m20*q1x + (q1x - q0x) / w;
  214.  *
  215.  *   m10*w + q0y = q1y*(m20*w + 1)
  216.  *   m10 = m20*q1y + (q1y - q0y) / w;
  217.  *
  218.  *   m01*h + q0x = q3x*(m21*h + 1)
  219.  *   m01 = m21*q3x + (q3x - q0x) / h;
  220.  *
  221.  *   m11*h + q0y = q3y*(m21*h + 1)
  222.  *   m11 = m21*q3y + (q3y - q0y) / h
  223.  *
  224.  *   m00*w +                 m01*h +                 q0x = q2x*(m20*w + m21*h + 1)
  225.  *
  226.  *   m20*q1x*w + q1x - q0x + m21*q3x*h + q3x - q0x + q0x = m20*q2x*w + m21*q2x*h + q2x
  227.  *
  228.  *   m20*q1x*w - m20*q2x*w = m21*q2x*h - m21*q3x*h + q2x - q1x + q0x - q3x + q0x - q0x
  229.  *
  230.  *   m20*(q1x - q2x)*w     = m21*(q2x - q3x)*h     + q2x - q1x - q3x + q0x
  231.  *
  232.  *
  233.  *   m10*w +                 m11*h +                 q0y = q2y*(m20*w + m21*h + 1)
  234.  *
  235.  *   m20*q1y*w + q1y - q0y + m21*q3y*h + q3y - q0y + q0y = m20*q2y*w + m21*q2y*h + q2y
  236.  *
  237.  *   m20*q1y*w - m20*q2y*w = m21*q2y*h - m21*q3y*h + q2y - q1y + q0y - q3y + q0y - q0y
  238.  *
  239.  *   m20*(q1y - q2y)*w     = m21*(q2y - q3y)*h     + q2y - q1y - q3y + q0y
  240.  *
  241.  *
  242.  *   m20*(q1x - q2x)*(q1y - q2y)*w     = m21*(q2x - q3x)*(q1y - q2y)*h     + (q2x - q1x - q3x + q0x)*(q1y - q2y)
  243.  *
  244.  *   m20*(q1y - q2y)*(q1x - q2x)*w     = m21*(q2y - q3y)*(q1x - q2x)*h     + (q2y - q1y - q3y + q0y)*(q1x - q2x)
  245.  *
  246.  *   0                                 = m21*((q2x - q3x)*(q1y - q2y) - (q2y - q3y)*(q1x - q2x))*h + (stuff)
  247.  *                                     = m21 * a + b;
  248.  *
  249.  *   m21 = -(stuff) / (other stuff)
  250.  *
  251.  *   m20 = f(m21)
  252.  *
  253.  *  m00 = f(m20)
  254.  *  m10 = f(m20)
  255.  *
  256.  *  m01 = f(m21)
  257.  *  m11 = f(m21)
  258.  *
  259.  * done.
  260.  */
  261. m_t solve (q_t q, real w, real h)
  262. {
  263.     real    q0x = q[0].x, q0y = q[0].y;
  264.     real    q1x = q[1].x, q1y = q[1].y;
  265.     real    q2x = q[2].x, q2y = q[2].y;
  266.     real    q3x = q[3].x, q3y = q[3].y;
  267.     real    m00, m01, m02;
  268.     real    m10, m11, m12;
  269.     real    m20, m21, m22;
  270.  
  271.     m02 = q0x;
  272.     m12 = q0y;
  273.     m22 = 1;
  274.  
  275.     real    a = ((q2x - q3x)*(q1y - q2y) - (q2y - q3y)*(q1x - q2x)) * h;
  276.     real    b = (q2x - q1x - q3x + q0x) * (q1y - q2y) - (q2y - q1y - q3y + q0y) * (q1x - q2x);
  277.     m21 = - b / a;
  278.  
  279.     if (q1x != q2x)
  280.     m20 = (m21 * (q2x - q3x) * h + q2x - q1x - q3x + q0x) / ((q1x - q2x) * w);
  281.     else
  282.     m20 = (m21 * (q2y - q3y) * h + q2y - q1y - q3y + q0y) / ((q1y - q2y) * w);
  283.  
  284.     m00 = m20 * q1x + (q1x - q0x) / w;
  285.     m10 = m20 * q1y + (q1y - q0y) / w;
  286.  
  287.     m01 = m21 * q3x + (q3x - q0x) / h;
  288.     m11 = m21 * q3y + (q3y - q0y) / h;
  289.  
  290.     return (m_t) {
  291.     { m00, m01, m02 },
  292.     { m10, m11, m12 },
  293.     { m20, m21, m22 } };
  294. }
  295.  
  296. m_t
  297. invert (m_t m)
  298. {
  299.     real  det;
  300.     int        i, j;
  301.     m_t    r;
  302.  
  303.     static int[3]    a = { 2, 2, 1 };
  304.     static int[3]    b = { 1, 0, 0 };
  305.  
  306.     det = 0;
  307.     for (i = 0; i < 3; i++) {
  308.     real    p;
  309.     int    ai = a[i];
  310.     int    bi = b[i];
  311.     p = m[i,0] * (m[ai,2] * m[bi,1] - m[ai,1] * m[bi,2]);
  312.     if (i == 1)
  313.         p = -p;
  314.     det += p;
  315.     }
  316.     det = 1/det;
  317.     for (j = 0; j < 3; j++) {
  318.     for (i = 0; i < 3; i++) {
  319.         real  p;
  320.         int        ai = a[i];
  321.         int        aj = a[j];
  322.         int        bi = b[i];
  323.         int        bj = b[j];
  324.  
  325.         p = m[ai,aj] * m[bi,bj] - m[ai,bj] * m[bi,aj];
  326.         if (((i + j) & 1) != 0)
  327.         p = -p;
  328.         r[j,i] = det * p;
  329.     }
  330.     }
  331.     return r;
  332. }
  333.  
  334. m_t
  335. rescale (m_t m, real limit)
  336. {
  337.     real    max = 0;
  338.     for (int j = 0; j < 3; j++)
  339.     for (int i = 0; i < 3; i++)
  340.         if ((real v = abs (m[j,i])) > max)
  341.         max = v;
  342.     real scale = limit / max;
  343.     for (int j = 0; j < 3; j++)
  344.     for (int i = 0; i < 3; i++)
  345.         m[j,i] *= scale;
  346.     return m;
  347.     
  348. }
  349.  
  350. string
  351. m_print (m_t m)
  352. {
  353.     /*
  354.     return sprintf ("%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f,%.8f",
  355.             m[0,0],m[0,1],m[0,2],
  356.             m[1,0],m[1,1],m[1,2],
  357.             m[2,0],m[2,1],m[2,2]);
  358.      */
  359.     return sprintf ("%v,%v,%v,%v,%v,%v,%v,%v,%v",
  360.             m[0,0],m[0,1],m[0,2],
  361.             m[1,0],m[1,1],m[1,2],
  362.             m[2,0],m[2,1],m[2,2]);
  363. }
  364.  
  365. int
  366. fixed (real x)
  367. {
  368.     return floor (x * 65536 + 0.5) & 0xffffffff;
  369. }
  370.  
  371. void
  372. m_print_fix (m_t m)
  373. {
  374.     for (int i = 0; i < 3; i++)
  375.     {
  376.     printf (" { 0x%08x, 0x%08x, 0x%08x },\n",
  377.         fixed (m[i,0]), fixed (m[i,1]), fixed (m[i,2]));
  378.     }
  379. }
  380.  
  381. string
  382. m_row (m_t m, int row)
  383. {
  384.     return sprintf ("%10.5f %10.5f %10.5f",
  385.             m[row,0],m[row,1],m[row,2]);
  386. }
  387.  
  388. Cairo::point_t[*] scale(Cairo::point_t[*] p, real w, real h)
  389. {
  390.     for (int i = 0; i < dim (p); i++) {
  391.     p[i].x *= w;
  392.     p[i].y *= h;
  393.     }
  394.     return p;
  395. }
  396.  
  397. typedef struct {
  398.     string  name;
  399.     rect_t  geometry;
  400. } output_t;
  401.  
  402. autoload Process;
  403.  
  404.  
  405. output_t[*] get_outputs () {
  406.     output_t[...]   outputs = {};
  407.     twixt (file randr = Process::popen (Process::popen_direction.read,
  408.                     false, "xrandr", "xrandr");
  409.        File::close (randr))
  410.     {
  411.     while (!File::end (randr)) {
  412.         string[*] words = String::wordsplit (File::fgets (randr), " ");
  413.         if (dim (words) >= 3 && words[1] == "connected" &&
  414.         File::sscanf (words[2], "%dx%d+%d+%d", 
  415.             &(int width), &(int height), 
  416.             &(int x), &(int y)) == 4)
  417.         {
  418.         outputs[dim(outputs)] = (output_t) {
  419.             name = words[0],
  420.             geometry = { 
  421.             x = x, y = y, width = width, height = height 
  422.             }
  423.         };
  424.         }
  425.     }
  426.     }
  427.     return outputs;
  428. }
  429.  
  430. void main ()
  431. {
  432.     m_t    m = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }, m_i, m_r;
  433.     bool m_available = true;
  434.     output_t[*]    outputs = get_outputs ();
  435.     output_t    target_output;
  436.  
  437.     if (dim (outputs) == 0) {
  438.     File::fprintf (stderr, "%s: No enabled outputs\n", argv[0]);
  439.     exit (1);
  440.     }
  441.     
  442.     if (dim (argv) > 1) {
  443.     int i;
  444.     for (i = 0; i < dim (outputs); i++)
  445.         if (argv[1] == outputs[i].name) {
  446.         target_output = outputs[i];
  447.         break;
  448.         }
  449.     if (i == dim (outputs)) {
  450.         File::fprintf (stderr, "%s: no enabled output \"%s\"\n",
  451.                argv[0], argv[1]);
  452.         exit (1);
  453.     }
  454.     }
  455.     else
  456.     target_output = outputs[0];
  457.     
  458.     real target_width = target_output.geometry.width;
  459.     real target_height = target_output.geometry.height;
  460.     
  461.     real screen_width = 0;
  462.     real screen_height = 0;
  463.  
  464.     for (int i = 0; i < dim (outputs); i++)
  465.     {
  466.     screen_width = max (screen_width, 
  467.                 outputs[i].geometry.x +
  468.                 outputs[i].geometry.width);
  469.     screen_height = max (screen_height, 
  470.                 outputs[i].geometry.y +
  471.                 outputs[i].geometry.height);
  472.     }
  473.  
  474.     &nichrome_t    nichrome = Nichrome::new ("Keystone Correction", 400, 350);
  475.  
  476.     (*label_t)[3]    label;
  477.     &label_t    space = Label::new (&nichrome, "");
  478.     for (int i = 0; i < 3; i++) {
  479.     label[i] = Label::new (&nichrome, "matrix");
  480.     label[i]->font = "sans-9";
  481.     }
  482.  
  483.     void callback (&quad_t quad) {
  484.     real    w = quad.geometry.width;
  485.     real    h = quad.geometry.height;
  486.     string[3]    text;
  487.     try {
  488.         m = solve (scale (quad.p, target_width / w, target_height / h),
  489.                target_width, target_height);
  490.         m_i = invert (m);
  491.         m_r = rescale (m_i, 16384);
  492.         for (int i = 0; i < 3; i++)
  493.         text[i] = m_row (m_i,i);
  494.         m_available = true;
  495.     } catch divide_by_zero (real a, real b) {
  496.         text = (string[3]) { "no solution", "" ... };
  497.         m_available = false;
  498.     }
  499.     for (int i = 0; i < 3; i++)
  500.         Label::relabel (label[i], text[i]);
  501.     }
  502.     &quad_t    quad = Quad::new (&nichrome, callback);
  503.  
  504.     void doit_func (&widget_t widget, bool state)
  505.     {
  506.     if (m_available)
  507.     {
  508.         Process::system ("xrandr",
  509.                  "xrandr",
  510.                  "--fb",
  511.                  sprintf ("%dx%d", screen_width, screen_height),
  512.                  "--output",
  513.                  target_output.name,
  514.                  "--transform",
  515.                  m_print (m_r));
  516.     }
  517.     }
  518.  
  519.     &button_t    doit = Button::new (&nichrome, "doit", doit_func);
  520.     
  521.     void show_func (&widget_t widget, bool state)
  522.     {
  523.     if (m_available)
  524.     {
  525.         printf ("normal:  %s\n", m_print (m));
  526.         printf ("inverse: %s\n", m_print (m_i));
  527.         printf ("scaled:  %s\n", m_print (m_r));
  528.         printf ("fixed:\n");
  529.         m_print_fix (m_i);
  530.     }
  531.     }
  532.     
  533.     &button_t    show = Button::new (&nichrome, "show", show_func);
  534.     &button_t    quit = Button::new (&nichrome, "quit",
  535.                      void func (&widget_t w, bool state) {
  536.                     w.nichrome.running = false;
  537.                      });
  538.  
  539.     &box_t    hbox = Box::new (Box::dir_t.horizontal,
  540.                  Box::widget_item (&doit, 0),
  541.                  Box::widget_item (&show, 0),
  542.                  
  543.                  Box::widget_item (&quit, 0),
  544.                  Box::glue_item (1));
  545.     &box_t    box = Box::new (Box::dir_t.vertical,
  546.                 Box::box_item (&hbox),
  547.                 Box::widget_item (label[0], 0),
  548.                 Box::widget_item (label[1], 0),
  549.                 Box::widget_item (label[2], 0),
  550.                 Box::widget_item (&space, 0),
  551.                 Box::widget_item (&quad, 1));
  552.     Nichrome::set_box (&nichrome, &box);
  553.     Nichrome::main_loop (&nichrome);
  554. }
  555.  
  556. main ();
  557.